﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////


#include "GnufLineStyleParameterDisplay.h"
#include "GnufParameterDisplayCustomFactory.h"
#include "GcmpBuiltInParameterDefinitions.h"
#include "GnufAppearanceSetting.h"
#include "GnufCommonLineStyleButton.h"
#include "GnufItem.h"
#include "GnufLineStyleButtonModel.h"
#include "GnufNameValuePairs.h"
#include "IDocument.h"
#include "IElement.h"
#include "IElementParameters.h"
#include "ILineTypeNew.h"
#include "IParameter.h"
#include "IParameterDefinitionLibrary.h"
#include "IParameterValueDouble.h"
#include "IParameterValueElementId.h"
#include "IParameterValueStorage.h"
#include "IParameterValueString.h"
#include <regex>
#include <stdlib.h>
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;
using namespace gnuf;
using namespace gfam;

namespace gfam
{
    static const int dropAreaHeight = 116; // 下拉区域显示高度，4个button
    const std::wstring LineStyleParameterDisplay::s_multiTypeText = GBMP_TR(L"*多种*");

    std::wstring GetFirstNumberString(std::wstring const & str)
    {
        wchar_t const* digits = L"0123456789";
        std::size_t const n = str.find_first_of(digits);
        if (n != std::string::npos)
        {
            std::size_t const m = str.find_first_not_of(digits, n);
            return str.substr(n, m != std::string::npos ? m - n : m);
        }
        return std::wstring();
    }

    void InsertMultiTypeButton(gnuf::CommonLineStyleButton *pButton)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pButton, L"pButton为空", L"paramodel", L"2024-02-20");
        auto pDropDownBoard = pButton->dropDownBoard();
        if (pDropDownBoard)
        {
            gnuf::Info info;
            info.setCaption(LineStyleParameterDisplay::s_multiTypeText);
            gcmp::OwnerPtr<gnuf::ButtonEx> button = gnuf::ButtonEx::make(gnuf::EItemType::ePushButton, pDropDownBoard);
            button->assignInfo(info);
            button->setUserData(pButton->userData());
            pDropDownBoard->insertMenuItem(0, TransferOwnership(button));
        }
    }

    void SetDropDownBoardUserData(gnuf::CommonLineStyleButton *pButton)
    {
        if (auto* pDropDownBoard = pButton->dropDownBoard())
        {
            gcmp::Any userData = pButton->userData();
            for (int i = 0; i < pDropDownBoard->menuItemCount(); ++i)
            {
                if (Item* item = pDropDownBoard->menuItem(i))
                {
                    item->setUserData(userData);
                }
            }
        }
    }

    int GetCurrentIndex(gnuf::CommonLineStyleButton* pButton, IParameter* pParameter)
    {
        int index = 0;
        if (auto* pDropDownBoard = pButton->dropDownBoard())
        {
            auto* pParameterDef = pParameter->GetParameterDefinition();
            DBG_WARN_AND_RETURN_UNLESS(pParameterDef, -1, L"pParameterDef为空", L"paramodel", L"2024-02-20");

            std::wstring lineTypeName;
            double lineWidth = -1;
            if (pParameterDef->GetStorageType() == ParameterStorageType::String)
            {
                lineTypeName = pParameter->GetValueAsString();
            }
            else if (pParameterDef->GetStorageType() == ParameterStorageType::Double)
            {
                lineWidth = pParameter->GetValueAsDouble();
            }
            else
            {
                DBG_WARN_AND_RETURN_UNLESS(false, -1, L"不存在对应类型", L"paramodel", L"2024-02-20");
            }

            for (int i = 0; i < pDropDownBoard->menuItemCount(); ++i)
            {
                Item* item = pDropDownBoard->menuItem(i);
                DBG_WARN_AND_RETURN_UNLESS(item, -1, L"item为空", L"paramodel", L"2024-02-20");

                auto* pInfo = item->info();
                DBG_WARN_AND_RETURN_UNLESS(pInfo, -1, L"pInfo为空", L"paramodel", L"2024-02-20");

                if (!lineTypeName.empty()) // 线型
                {
                    if (lineTypeName == pInfo->caption())
                    {
                        index = i;
                        break;
                    }
                }
                else // 线宽
                {
                    std::wstring caption = pInfo->caption();
                    std::wstring numberString = GetFirstNumberString(caption);
                    if (!numberString.empty())
                    {
                        double oldLineWidth = std::stof(numberString);
                        if (abs(lineWidth - oldLineWidth) < 0.0001)
                        {
                            index = i;
                            break;
                        }
                    }
                }
            }
        }
        return index;
    }
}

gfam::LineStyleParameterDisplay::LineStyleParameterDisplay()
    : m_currentValue(nullptr)
    , m_pCommonLineStyleButton(nullptr)
    , m_isLineType(false)
    , m_isLineWidth(false)
    , m_opLineStyleBtnModel(nullptr)
{
}

gnuf::Item * gfam::LineStyleParameterDisplay::OnDisplay(gnuf::NameValuePairBlock * pNameValuePairBlock, gcmp::IElement * pElement, int parameterId)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pNameValuePairBlock, L"pNameValuePairBlock为空", L"paramodel", L"2024-02-20");
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElement, L"pElement为空", L"paramodel", L"2024-02-20");
    SetOwnerElements({ pElement });

    m_pCommonLineStyleButton = GetLineStyleButton(pElement, pNameValuePairBlock, parameterId); // 函数内部有返回值判空
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pCommonLineStyleButton, L"m_pCommonLineStyleButton为空", L"paramodel", L"2024-02-20");
    m_pCommonLineStyleButton->setCurrentIndex(GetCurrentIndex(m_pCommonLineStyleButton, GetParameter(pElement, parameterId).get()));

    return m_pCommonLineStyleButton;
}

gnuf::Item * gfam::LineStyleParameterDisplay::OnDisplay(gnuf::NameValuePairBlock * pNameValuePairBlock, const std::vector<gcmp::IElement*>& elements, int parameterId)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pNameValuePairBlock, L"pNameValuePairBlock为空", L"paramodel", L"2024-02-20");
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(!elements.empty(), L"elements为空", L"paramodel", L"2024-02-20");
    for (auto one : elements) DBG_WARN_AND_RETURN_NULLPTR_UNLESS(one, L"Empty pointer!", L"paramodel", L"2024-02-20");

    SetOwnerElements(elements);

    m_pCommonLineStyleButton = GetLineStyleButton(elements[0], pNameValuePairBlock, parameterId); // 函数内部有返回值判空
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pCommonLineStyleButton, L"m_pCommonLineStyleButton为空", L"paramodel", L"2024-02-20");

    // Set the display text if multi type.
    if (elements.size() > 1)
    {
        InsertMultiTypeButton(m_pCommonLineStyleButton);
        m_pCommonLineStyleButton->setCurrentIndex(0);
    }

    return m_pCommonLineStyleButton;
}

void gfam::LineStyleParameterDisplay::onClicked(gnuf::Item* pItem)
{
    auto pElement = GetOwnerElement();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pElement, L"pElement为空", L"paramodel", L"2024-02-20");
    IDocument* pDoc = pElement->GetDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空", L"paramodel", L"2024-02-20");
    auto* pInfo = pItem->info();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pInfo, L"pInfo为空", L"paramodel", L"2024-02-20");

    if (m_isLineType)
    {
        std::wstring lineTypeName = pInfo->caption();
        if (lineTypeName.empty())
        {
            return;
        }

        SetCurrentValueStorage(IParameterValueString::Create(lineTypeName));
    }
    else if (m_isLineWidth)
    {
        std::wstring caption = pInfo->caption();
        std::wstring numberString = GetFirstNumberString(caption);
        if (numberString.empty())
        {
            return;
        }
        double lineWidth = std::stof(numberString);
        SetCurrentValueStorage(IParameterValueDouble::Create(lineWidth));
    }
}

gcmp::OwnerPtr<gcmp::IParameterValueStorage> gfam::LineStyleParameterDisplay::GetValue(IDocument* pDoc, const std::vector<gcmp::ElementId>& elements, const gnuf::Item* pItem) const
{
    if (nullptr == m_currentValue.get())
        return nullptr;

    return m_currentValue ? m_currentValue->Clone() : IParameterValueElementId::Create(ElementId::InvalidID);
}

gcmp::IElement * gfam::LineStyleParameterDisplay::GetOwnerElement() const
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(!m_elements.empty(),
        L"No any element exists!", L"paramodel", L"2024-02-20");
    return m_elements[0];
}

void gfam::LineStyleParameterDisplay::SetOwnerElements(const std::vector<gcmp::IElement *> & elements)
{
    m_elements = elements;
}

gnuf::CommonLineStyleButton* gfam::LineStyleParameterDisplay::GetLineStyleButton(gcmp::IElement * pElement, gnuf::NameValuePairBlock * pNameValuePairBlock, int parameterId)
{
    // 设置当前值
    IDocument* pDoc = pElement->GetDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空", L"paramodel", L"2024-02-20");
    OwnerPtr<IParameter> opParam = GetParameter(pElement, parameterId);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opParam, L"opParam为空", L"paramodel", L"2024-02-20");
    const IParameterValueStorage* pStorage = opParam->GetParameterValueStorage();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pStorage, L"pStorage为空", L"paramodel", L"2024-02-20");
    SetCurrentValueStorage(pStorage->Clone());

    m_opLineStyleBtnModel = GetLineStyleModel(pDoc, opParam.get());
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_opLineStyleBtnModel, L"m_opLineStyleBtnModel为空", L"paramodel", L"2024-02-20");

    // 类型也只需要设置一次
    if (opParam->GetParameterDefinitionUid() == PARAMETER_UID(LineTypeBuiltInParameter))
    {
        m_isLineType = true;
    }
    else if (opParam->GetParameterDefinitionUid() == PARAMETER_UID(LineWidthBuiltInParameter))
    {
        m_isLineWidth = true;
    }

    gnuf::CommonLineStyleButton* pLineStyleButton = pNameValuePairBlock->addItem<gnuf::CommonLineStyleButton>(gnuf::Info(opParam->GetName()));
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLineStyleButton, L"pLineStyleButton为空", L"paramodel", L"2024-02-20");
    pLineStyleButton->setModel(m_opLineStyleBtnModel.get());

    // 设置下拉区域的宽度
    const AppearanceSetting* pAppearanceSetting = pLineStyleButton->appearanceSetting();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pAppearanceSetting, L"pAppearanceSetting为空", L"paramodel", L"2024-02-20");
    const DropDownBoardAppearanceSetting* settingConst = pAppearanceSetting->dropDownBoardAppearanceSetting();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(settingConst, L"settingConst为空", L"paramodel", L"2024-02-20");
    DropDownBoardAppearanceSetting* setting = const_cast<DropDownBoardAppearanceSetting*>(settingConst);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(setting, L"setting为空", L"paramodel", L"2024-02-20");
    setting->setHeight(dropAreaHeight);

    const gcmp::ElementId elementId = pElement->GetElementId();
    ItemInfo pInfoList(elementId, true, parameterId);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pLineStyleButton->parent() && pNameValuePairBlock->info(), L"pList为空", L"paramodel", L"2024-02-20");
    pLineStyleButton->setQtObjectName(StringUtil::ToWString(pLineStyleButton->parent()->path().str()) + L"/" + pNameValuePairBlock->info()->caption() + L"$" + opParam->GetName());
    pLineStyleButton->setUserData(gcmp::Any(pInfoList));
    SetDropDownBoardUserData(pLineStyleButton);

    return pLineStyleButton;
}

OwnerPtr<IParameter> gfam::LineStyleParameterDisplay::GetParameter(gcmp::IElement* pElement, int parameterId)
{
    const IElementParameters* pParameter = pElement->GetElementParameters();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pParameter, L"pParameter为空", L"paramodel", L"2024-02-20");
    return TransferOwnership(pParameter->GetParameterById(parameterId));
}

gcmp::OwnerPtr<gnuf::CommonLineStyleButtonModel> gfam::LineStyleParameterDisplay::GetLineStyleModel(IDocument* pDoc, IParameter* pParam)
{
    auto pBtnModelFactory = gnuf::LineStyleButtonModelFactory::getInstance(pDoc);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(nullptr != pBtnModelFactory, L"pBtnModelFactory为空", L"paramodel", L"2024-02-20");

    std::vector<ILineTypeNew*> lineTypes = ILineTypeNew::GetAllLineTypes(pDoc);
    std::vector<const gcmp::ILineTypeNew*> lintTypeDatas;
    FOR_EACH(pCurLineType, lineTypes)
    {
        lintTypeDatas.push_back(pCurLineType);
    }
    pBtnModelFactory->setGraphicsStyleIds({ ElementId::InvalidID }, lintTypeDatas); // { ElementId::InvalidID }添加一个无效参数，该参数对应的model没有用到

    if (pParam->GetParameterDefinitionUid() == PARAMETER_UID(LineTypeBuiltInParameter))
    {
        return TransferOwnership(pBtnModelFactory->make(gnuf::LineStyleButtonModelFactory::eLinePattern)); // 线型
    }
    else if (pParam->GetParameterDefinitionUid() == PARAMETER_UID(LineWidthBuiltInParameter))
    {
        return TransferOwnership(pBtnModelFactory->make(gnuf::LineStyleButtonModelFactory::eLineWidth)); // 线宽
    }
    return nullptr;
}

GnufParameterDisplayCustomCreator s_lineTypeNameParameterDisplayCreator(
    PARAMETER_UID(LineTypeBuiltInParameter), NEW_AS_OWNER_PTR(LineStyleParameterDisplay));
GnufParameterDisplayCustomCreator s_lineWidthNameParameterDisplayCreator(
    PARAMETER_UID(LineWidthBuiltInParameter), NEW_AS_OWNER_PTR(LineStyleParameterDisplay));
